#include <dirent.h>
#include <omp.h>
#include <sys/stat.h>
#include <unistd.h>

#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <thread>
#include <vector>

using namespace std;

std::string a(".");
std::string tow_dot("..");

/* 2. 直接文件夹级别。
 * 将直接文件夹（如dataset/000/009/）dir_path中的文件合一到out_txt_path（如output/000/009.txt）文件中
 */
void process_dir(std::string in_direct_dir_path, std::string out_txt_path) {
  // 这三个变量只有这里才会被用到
  const string lat_start = " lat=\"";
  const string lon_start = "\" lon=\"";
  const string lon_end = "\">";

  // 输入输出流
  ofstream ofs;
  ifstream ifs;
  ofs.open(
      out_txt_path);  //这个是输出流的open，应该判断是文件表位置、位图等够不够，而不是文件是否已存在
  if (ofs.fail()) {
    cout << "can't open file " << out_txt_path << endl;
    std::abort();
  }

  cout << "opening: " << in_direct_dir_path
       << ", saving to ->: " << out_txt_path << endl;

  DIR* direct_dir_open;  //定义一个目录指针
  /* 打开目录，先判断一下, 注意opendir接收的是const char* 类型 */
  if ((direct_dir_open = opendir(in_direct_dir_path.c_str())) == NULL) {
    cout << "in valid dir:" << in_direct_dir_path << endl;
    std::abort();
  }
  /* 没问题的话开始遍历dir */
  struct dirent* direct_dirp;
  while ((direct_dirp = readdir(direct_dir_open)) !=
         NULL) {  //最后一步了，遍历最里层文件夹
    if (direct_dirp->d_type == DT_REG && direct_dirp->d_name != a &&
        direct_dirp->d_name != tow_dot) {
      // 得到文件绝对路径
      char filepath[512];
      memset(filepath, 0, sizeof(filepath));
      strcpy(filepath, in_direct_dir_path.c_str());
      strcat(filepath, "/");
      strcat(filepath, direct_dirp->d_name);  //此时dirbuf已经是完整路径了
      ifs.open(filepath);

      string line;
      while (getline(ifs, line)) {
        std::regex reg(".*<trkpt lat=.*lon=.*>.*");
        if (std::regex_match(line, reg)) {
          int i = line.find(lat_start);
          int j = line.find(lon_start);
          int k = line.find(lon_end);
          auto lat_i = i + lat_start.size();
          auto lat_str = line.substr(lat_i, j - lat_i);
          auto lon_i = j + lon_start.size();
          auto lon_str = line.substr(lon_i, k - lon_i);
          ofs << lat_str << " " << lon_str << endl;
        }
      }
      ifs.close();
    } else if (direct_dirp->d_name == a || direct_dirp->d_name == tow_dot) {
      //			cout << "omitting . and/or .." << endl;    //
      //直接文件夹（二级文件夹）也要考虑. ..目录项
    } else {
      cout << "error 2nd level directory..." << endl;
      std::abort();
    }
  }
  ofs.close();
}

/* 1. 子文件夹级别。
 * 将子文件夹（如dataset/000/，也即一级间接文件夹）in_sub_dir
 * 中的直接文件夹名；拼接出子文件夹中的多个直接文件夹中的众文件要被整理到的txt路径
 */
void process_basedir(std::string in_sub_dir_path,
                     std::string out_sub_dir_path) {
  /* 同样，打开前先判断 */
  DIR* in_subdir_open;
  if ((in_subdir_open = opendir(in_sub_dir_path.c_str())) == NULL) {
    cout << "in valid insub dir:" << in_sub_dir_path << endl;
    std::abort();
  }

  struct dirent* subdir_p;
  vector<std::string> all_input_direct_dirnames;
  while ((subdir_p = readdir(in_subdir_open)) != NULL) {
    if (subdir_p->d_type == DT_DIR && subdir_p->d_name != a &&
        subdir_p->d_name != tow_dot) {  // subdir也要单独掕出 . ..
      cout << "in sub dir: " << in_sub_dir_path
           << ", collecting: " << subdir_p->d_name << endl;
      all_input_direct_dirnames.push_back(subdir_p->d_name);
    } else if (subdir_p->d_name == a || subdir_p->d_name == tow_dot) {
      //			cout << "omitting . and/or .." << endl;
    } else {
      cout << "error 2nd level directory..." << endl;
      std::abort();
    }
  }

  int n = all_input_direct_dirnames.size();
#pragma omp parallel for
  for (int i = 0; i < n; ++i) {
    /* 即是输入直接文件夹，也是输出直接文件夹 */
    std::string single_direct_dirname =
        all_input_direct_dirnames[i];  //少用auto

    // 	/* 输入文件夹路径 */
    char in_direct_dir_path[512];
    memset(in_direct_dir_path, 0, sizeof(in_direct_dir_path));
    strcpy(in_direct_dir_path, in_sub_dir_path.c_str());
    strcat(in_direct_dir_path, "/");
    strcat(in_direct_dir_path,
           single_direct_dirname.c_str());  //此时dirbuf已经是完整路径了

    // 	/* 输出txt路径 */
    auto out_txt_path = out_sub_dir_path;
    out_txt_path.append("/" + single_direct_dirname + ".txt");

    /*direct dir粒度的, 输入文件夹，输出文件路径*/
    process_dir(in_direct_dir_path, out_txt_path);
  }
}

/* 0. dataset级别。
 * 大文件夹的输入输出接口*/
int process_dataset(string dataset_path, const string kOutputDir) {
  /* 输入文件夹一定要有，否则提前结束；输出文件夹没有的话新建 */
  DIR* dataset_dir_open;  //定义一个目录指针
  if ((dataset_dir_open = opendir(dataset_path.c_str())) == NULL) {
    cout << "in valid dataset dir:" << dataset_path << endl;
    std::abort();
  }

  auto kOutputDir_open = opendir(kOutputDir.c_str());
  if (kOutputDir_open == NULL) {
    mkdir(kOutputDir.c_str(), ACCESSPERMS);
  }

  struct dirent* subdir_open;
  while ((subdir_open = readdir(dataset_dir_open)) != NULL) {  //遍历
    if (subdir_open->d_type == DT_DIR && subdir_open->d_name != a &&
        subdir_open->d_name != tow_dot) {
      /* 构造输出文件夹 */
      char out_sub_dir[512];
      memset(out_sub_dir, 0, sizeof(out_sub_dir));
      strcpy(out_sub_dir, kOutputDir.c_str());
      strcat(out_sub_dir, "/");
      strcat(out_sub_dir, subdir_open->d_name);  //此时dirbuf已经是完整路径了
      auto out_dir_open = opendir(out_sub_dir);
      if (out_dir_open == NULL) {
        cout << "构造输出子文件夹" << std::string(out_sub_dir) << endl;
        mkdir(out_sub_dir, ACCESSPERMS);
      }
      //			cout << "out sub dir: " <<
      // std::string(out_sub_dir) <<endl;

      /* 逐个处理输入文件夹 */
      char in_sub_dir[512];
      cout << "in dataset/ : " << subdir_open->d_name << endl;
      memset(in_sub_dir, 0, sizeof(in_sub_dir));
      strcpy(in_sub_dir, dataset_path.c_str());
      strcat(in_sub_dir, "/");
      strcat(in_sub_dir, subdir_open->d_name);  //此时dirbuf已经是完整路径了

      /* process处理的是indirect dir，将inputdir里面的所有文件整理到outputdir */
      process_basedir(in_sub_dir, out_sub_dir);

    } else if (subdir_open->d_name == a || subdir_open->d_name == tow_dot) {
      //			cout << "omitting . and/or .." << endl;
    } else {
      cout << "Error 1st level directory..." << endl;
      std::abort();
    }
  }

  return 0;
}

int main(int argc, char* argv[]) {
  cout << "starting, printing may be mutually influenced" << endl;
  if (argc < 2) {
    cout << "Enter the base directory..." << endl;
    std::abort();
  }
  /* 输入、输出文件夹 */
  string dataset_path = argv[1];
  const string kOutputDir = "./output";

  /* 处理 */
  process_dataset(dataset_path, kOutputDir);
  return 0;
}
